<?php
/**
 * +--------------------------------------------------------+
 * @Created by Phpstorm.
 * @Category Container.php
 * @Depiction
 * @Author hwy <429208948@qq.com>
 * @Date 2022/5/23  11:30
 * +--------------------------------------------------------+
 */

namespace App\Util;


class Container
{
    protected static $_singleton = [];

    /**
     * Container constructor.
     * 定义一个私有的构造函数，确保单例类不能通过new关键字实例化，只能被其自身实例化
     */
    private function __construct()
    {
    }

    // 获取一个单例实例
    private static function getSingleton($class_name)
    {
        return array_key_exists($class_name, self::$_singleton) ?
            self::$_singleton[$class_name] : NULL;
    }

     /**
     * @param $class
     * @return object
     * @throws \ReflectionException
     * @Depiction 依赖实例
     * @Author hwy <429208948@qq.com>
     * @Date 2022/5/23  14:26
     */
    private static function getInstance($class)
    {
        // 获取反射实例
        $reflector   = new \ReflectionClass($class);
        // 获取反射实例的构造方法
        $constructor = $reflector->getConstructor();

        if (is_null($constructor)) {
            if ($reflector->isInstantiable()) {
                return $reflector->newInstance();
            }
        }

        $dependenciesArgs = [];
        $dependencies     = $constructor->getParameters();//获得构造方法的参数

        foreach ($dependencies as $dependency) {
            //实例化依赖
            // $class = $dependency->getType()->getName();
            $class = $dependency->getClass();

            if ($class) {
                // 如果依赖是单例，则直接获取，反之创建实例
                $singleton = self::getSingleton($class->name);
                $dependenciesArgs[] = $singleton ? $singleton : self::getInstance($class->name);
            }
        }

        return $reflector->newInstanceArgs($dependenciesArgs);
    }

    /**
     * @param $class_name
     * @param $method
     * @param array $params
     * @return false|mixed
     * @throws \ReflectionException
     * @Depiction 增加 run 方法
     * @Author hwy <429208948@qq.com>
     * @Date 2022/5/23  16:16
     */
    public static function run($class_name, $method, $params = [])
    {
        if ( ! class_exists($class_name)) {
            throw new \BadMethodCallException("Class $class_name is not found!");
        }

        if ( ! method_exists($class_name, $method)) {
            throw new \BadMethodCallException("undefined method $method in $class_name !");
        }
        // 获取外层实例 new $class_name
        $instance = self::getInstance($class_name);

        //以下是为了获取 $method 方法的参数
        // 通过反射实例，获取 $class_name 类的相关方法和属性等
        $reflector = new \ReflectionClass($class_name);
        // 获取方法
        $reflectorMethod = $reflector->getMethod($method);

        $dependenciesArgs = [];
        $dependencies     = $reflectorMethod->getParameters();//获得构造方法的参数

        // 查找方法的形参 $method
        foreach ($dependencies as $param) {
            $class = $param->getClass(); // 如果类，则实例
            if ($class) {
                $singleton = self::getSingleton($class->name);
                $dependenciesArgs[] = $singleton ? $singleton : self::getInstance($class->name);
            }
        }

        // 运行方法
        return call_user_func([$instance, $method], array_merge($dependenciesArgs, $params));
    }


    /**
     * 非单例模式
     */
    public static function getInstances($class)
    {

        // 获取反射实例
        $reflector   = new \ReflectionClass($class);
        // 获取反射实例的构造方法
        $constructor = $reflector->getConstructor();

        if (is_null($constructor)) {
            if ($reflector->isInstantiable()) {
                return $reflector->newInstance();
            }
        }

        $dependenciesArgs = [];
        $dependencies     = $constructor->getParameters();//获得构造方法的参数

        foreach ($dependencies as $dependency) {
            //实例化依赖
            // $class = $dependency->getType()->getName();
            $class = $dependency->getClass();

            if ($class) {
                $dependenciesArgs[] = self::getInstance($class->name);
            }
        }

        return $reflector->newInstanceArgs($dependenciesArgs);
    }
}
